﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Threading;

namespace DelicateDict.Classes
{
    class HookingWord
    {
        public delegate IntPtr HookProcDelegate(int nCode, IntPtr wParam, IntPtr lParam);

        public HookingWord()
        {
            InstallHook();
        }
        ~HookingWord()
        {
            UnInstallHook();
        }
        //global variables
        private const int WH_KEYBOARD_LL = 13;
        private const int WH_MOUSE_LL = 14;
        private const int WM_KEYDOWN = 0x0100;
        private const int WM_CLOSE = 0x10;
        private const int WM_KILLFOCUS = 0x0008;
        private const int WM_GETTEXT = 0x0D;
        private const int WM_GETTEXTLENGTH = 0x0E;
        private const int EM_GETSEL = 0xB0;
        private const int WM_COPY = 0x0301;
        private const int EM_SETSEL = 0x00B1;
        private const int WM_USER = 0x0400;
        private const int EM_GETSELTEXT = 1086;
        private const int WM_DESTROYCLIPBOARD = 0x0307;
        private const int VK_LBUTTON = 0x01;


        IntPtr hKeyboardHook = IntPtr.Zero;
        IntPtr hMouseHook = IntPtr.Zero;
        HookProcDelegate myKeyProc;
        HookProcDelegate myMouseProc;

        public Point pos;

        private bool isMouseDown = false;


        [StructLayout(LayoutKind.Sequential)]
        private struct MSLLHOOKSTRUCT
        {
            public Point pt;
            public uint mouseData;
            public uint flags;
            public uint time;
            public IntPtr dwExtraInfo;
        }
        private enum MouseMessages
        {
            WM_MOUSEMOVE = 0x0200,
            WM_LBUTTONDOWN = 0x0201,
            WM_LBUTTONUP = 0x0202,
            WM_RBUTTONDOWN = 0x0204,
            WM_RBUTTONUP = 0x0205,
            WM_MOUSEWHEEL = 0x020A
        }

        static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);

        //Import functions
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr SetWindowsHookEx(int idHook, HookProcDelegate lpfn, IntPtr hInstance, uint ThreadID);
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr CallNextHookEx(IntPtr hHook, int nCode, IntPtr wParam, IntPtr lParam);
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool UnhookWindowsHookEx(IntPtr hHook);
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr GetModuleHandle(string name);
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool GetCursorPos(ref Point pt);
        [DllImport("user32.dll")]
        public static extern IntPtr WindowFromPoint(Point lpPoint);
        [DllImport("User32.dll")]
        private static extern bool SetForegroundWindow(IntPtr hWnd);
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        static public extern IntPtr GetForegroundWindow();
        [DllImport("user32.dll")]
        static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, uint dwExtraInfo);
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr SendMessage(IntPtr hWnd, int mes, IntPtr wParam, IntPtr lParam);
        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        public static extern short GetAsyncKeyState(int vkey);


        public IntPtr HookKeyProc(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if ((nCode >= 0) && (wParam == (IntPtr)WM_KEYDOWN))
            {
                if (((Keys)Marshal.ReadInt32(lParam)) == Keys.T)
                {
                    if ((Control.ModifierKeys & Keys.Control) == Keys.Control)
                    {
                        //If the previous instance is existing, ClosePrevious will be call
                        ClosePrevWindow();
                        if (CopyWord())
                        {
                            PasteWord();
                        }
                        //discard the input message to the owner window
                        return (IntPtr)1;
                    }
                }
            }
            return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
        }
        public IntPtr HookMouseProc(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0)
            {
                switch ((MouseMessages)wParam)
                {
                    case MouseMessages.WM_LBUTTONUP:
                        if (isMouseDown == true)
                        {
                            MSLLHOOKSTRUCT mouseStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
                            pos.X = mouseStruct.pt.X;
                            pos.Y = mouseStruct.pt.Y;
                            isMouseDown = false;
                        }
                        break;
                    case MouseMessages.WM_MOUSEMOVE:
                        if ((GetAsyncKeyState(VK_LBUTTON) & 0x8000) == 0x8000)
                            isMouseDown = true;
                        break;
                }
            }
            return CallNextHookEx(hMouseHook, nCode, wParam, lParam);
        }
        public void ClosePrevWindow()
        {
            if (ClickAndSeeForm.curHandle != IntPtr.Zero)
            {
                ClickAndSeeForm prevForm = (ClickAndSeeForm)Control.FromHandle(ClickAndSeeForm.curHandle);
                prevForm.Close();
            }
        }
        public bool CopyWord()
        {
            try
            {
                Point pt = new Point(0, 0);
                GetCursorPos(ref pt);
                IntPtr activeWindowHandle = GetWindowUnderCursor(ref pt);
                GetSelectedText(ref activeWindowHandle);
                return true;
            }
            catch (System.Exception ex)
            {
                MessageBox.Show(ex.Message);
                return false;
            }
        }
        public void PasteWord()
        {

            ClickAndSeeForm mForm = new ClickAndSeeForm();
            ClickAndSeeForm.curHandle = mForm.Handle;
            mForm.DesktopLocation = new Point(pos.X + 1, pos.Y + 1);

            SetForegroundWindow(mForm.Handle);

            IDataObject iData = Clipboard.GetDataObject();
            if (iData.GetDataPresent(DataFormats.Text))
                mForm.ShowClickAndSee((String)iData.GetData(DataFormats.Text));

            mForm.Show();
        }

        public bool InstallHook()
        {

            using (System.Diagnostics.Process curProc = System.Diagnostics.Process.GetCurrentProcess())
            using (System.Diagnostics.ProcessModule curMod = curProc.MainModule)
            {
                myKeyProc = new HookProcDelegate(HookKeyProc);
                hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, myKeyProc, GetModuleHandle(curMod.ModuleName), 0);
                if (hKeyboardHook == IntPtr.Zero)
                    return false;
                myMouseProc = new HookProcDelegate(HookMouseProc);
                hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, myMouseProc, GetModuleHandle(curMod.ModuleName), 0);
                if (hMouseHook == IntPtr.Zero)
                    return false;
                return true;
            }
        }
        public bool UnInstallHook()
        {

            if (hKeyboardHook != IntPtr.Zero)
            {
                if (!UnhookWindowsHookEx(hKeyboardHook))
                {
                    return false;
                }
            }
            if (hMouseHook != IntPtr.Zero)
            {
                if (!UnhookWindowsHookEx(hMouseHook))
                {
                    return false;
                }
            }
            hKeyboardHook = IntPtr.Zero;
            hMouseHook = IntPtr.Zero;
            //MessageBox.Show("Go bo thanh cong");
            return true;
        }

        private static void GetSelectedText(ref IntPtr hWnd)
        {
            //SendMessage(hWnd, WM_COPY, IntPtr.Zero, IntPtr.Zero);

            //SetForegroundWindow(hWnd);
            SendKeys.SendWait("^c");
        }

        public static IntPtr GetWindowUnderCursor(ref Point ptCursor)
        {
            return WindowFromPoint(ptCursor);
        }
    }

}
